#pragma once

#include <iostream>
#include <memory>
#include <mutex>
#include <cassert>
#include <unordered_map>
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_msg.pb.h"


namespace bitmq
{
    // 1. 定义绑定信息类
    struct Binding
    {
        Binding()
        {}

        using ptr = std::shared_ptr<Binding>;
        Binding(const std::string& ename, const std::string& qname, 
                const std::string& key, bool dur)
            : exchange_name(ename)
            , msgqueue_name(qname)
            , binding_key(key)
            , durable(dur)
        {}

        std::string exchange_name;  // 1）交换机名称
        std::string msgqueue_name;  // 2）队列名称
        std::string binding_key;    // 3）binding_key（分发匹配规则-决定了哪些数据能被交换机放入队列）
        bool durable;               // 4）是否需要持久化
    };

    // 2. 定义绑定信息数据持久化类（数据持久化的 sqlite3 数据库中）
    class BindingMapper
    {
    public:
        //队列和绑定信息是一一对应的
        using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
        //一个交换机可以绑定多个队列，所以key为交换机，value为队列和绑定信息表
        using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;
    public:
        BindingMapper(const std::string& dbfile)
            : _sql_helper(dbfile)
        {
            std::string path = bitmq::FileHelper::parentDirectory(dbfile);
            bitmq::FileHelper::createDirectory(path);
            assert(_sql_helper.open());
            createTable();
        }

        // 1）创建绑定信息数据表
        void createTable()
        {
            std::string sql = "create table if not exists binding_table(exchange_name varchar(32), \
                msgqueue_name varchar(32),  binding_key varchar(128), durable int);";
            assert(_sql_helper.exec(sql, nullptr, nullptr));
        }

        // 2）删除绑定信息数据表 
        void removeTable()
        {
            std::string sql = "drop table if exists binding_table";
            assert(_sql_helper.exec(sql, nullptr, nullptr));
        }

        // 2）新增绑定信息数据
        bool insert(Binding::ptr& binding)
        {
            std::stringstream ss;
            ss << "insert into binding_table values(";
            ss << "'" << binding->exchange_name << "', ";
            ss << "'" << binding->msgqueue_name << "', ";
            ss << "'" << binding->binding_key << "', ";
            ss << binding->durable << ");";
            return _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        // 3）移除指定绑定信息数据
        void remove(const std::string& ename, const std::string& qname)
        {
            std::stringstream ss;
            ss << "delete from binding_table where "; 
            ss << "exchange_name = '" << ename << "' and ";
            ss << "msgqueue_name = '" << qname << "';";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        // 4）移除指定交换机相关绑定信息数据：移除交换机的时候会被调用  
        void removeExchangeBinding(const std::string& ename)
        {
            std::stringstream ss;
            ss << "delete from binding_table where "; 
            ss << "exchange_name = '" << ename << "';";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        // 5）移除指定队列相关绑定信息数据：移除队列的时候会被调用
        void removeQueueBinding(const std::string& qname)
        {
            std::stringstream ss;
            ss << "delete from binding_table where "; 
            ss << "msgqueue_name = '" << qname << "';";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        // 6）查询所有绑定信息数据：用于重启服务器时进行历史数据恢复
        BindingMap recovery()
        {
            std::string sql = "select exchange_name, msgqueue_name, binding_key, durable from binding_table";
            BindingMap result;
            _sql_helper.exec(sql, selectAllCallback, &result);
            return result;
        }

    private:
        static int selectAllCallback(void* arg, int numCol, char** row, char** fields)
        {
            BindingMap* result = (BindingMap*)arg;
            auto bp = std::make_shared<Binding>();
            bp->exchange_name = row[0];
            bp->msgqueue_name = row[1];
            bp->binding_key = row[2];
            bp->durable = (bool)std::stoi(row[3]);

            //获取交换机对应的队列绑定映射
            MsgQueueBindingMap& qmap = (*result)[bp->exchange_name];
            //向队列绑定映射中他添加信息
            qmap.insert({bp->msgqueue_name, bp});
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    // 3. 定义绑定信息数据管理类
    using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
    class BindingManager
    {
    public:
        //队列和绑定信息是一一对应的
        using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
        //一个交换机可以绑定多个队列，所以key为交换机，value为队列和绑定信息表
        using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;
        using ptr = std::shared_ptr<BindingManager>;
    public:
        BindingManager(const std::string& dbfile)
            : _mapper(dbfile)
        {
            //恢复历史消息
            _bindings = _mapper.recovery();
        }

        // 1）创建绑定信息，并添加管理（存在则 OK，不存在则创建）
        bool bind(const std::string& ename, const std::string& qname, const std::string& key, bool durable)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if (it != _bindings.end() && it->second.find(qname) != it->second.end())
                return true;

            Binding::ptr bp = std::make_shared<Binding>(ename, qname, key, durable);
            if (durable == true)
            {
                if (_mapper.insert(bp) == false)
                    return false;
            }
            auto& qbmp = _bindings[ename];
            qbmp.insert({qname, bp});
            return true;
        }

        // 2）解除指定的绑定信息
        void unBind(const std::string& ename, const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if (it == _bindings.end())   //没有交换机相关信息
                return;
            if (it->second.find(qname) == it->second.end())  //交换机没有队列相关绑定信息
                return;
            if (it->second[qname]->durable == true)
                _mapper.remove(ename, qname);
            it->second.erase(qname);
        }

        // 3）删除交换机相关的所有绑定信息
        void removeExchangeBindings(const std::string& ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeExchangeBinding(ename);
            _bindings.erase(ename);
        }

        // 4）删除指定队列的所有绑定信息
        void removeMsgQueueBindings(const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeQueueBinding(qname);
            //遍历每个交换机，移除所有指定 队列相关信息
            for (auto& it : _bindings)
            {
                it.second.erase(qname);
            }
        }

        // 5）获取交换机相关的所有绑定信息：交换机收到消息后，需要分发给自己关联的队列
        MsgQueueBindingMap getExchangeBindings(const std::string& ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if (it == _bindings.end())
                return {};
            return it->second;
        }

        // 6）获取指定绑定信息
        Binding::ptr getBinding(const std::string& ename, const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if (it == _bindings.end())
                return {}; 
            auto qit = it->second.find(qname);
            if (qit == it->second.end())
                return {};
            return qit->second;
        }

        // 7）判断指定绑定信息是否存在
        bool exists(const std::string& ename, const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if (it == _bindings.end())
                return false; 
            auto qit = it->second.find(qname);
            if (qit == it->second.end())
                return false;
            return true;
        }

        // 8）获取当前绑定信息数量
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            int total_size = 0;
            for (auto it : _bindings)
            {
                total_size += it.second.size();
            }
            return total_size;
        }

        // 9）销毁所有绑定信息数据
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _bindings.clear();
        }

    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;
    };
}